package edu.caece.langprocessor.syntax.tree;

import java.util.List;

import edu.caece.langprocessor.syntax.tree.nodes.AddTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.AssignmentTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.BeginTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.CallTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionEqualsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionEvenTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionGreaterEqualsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionGreaterTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionLowerEqualsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionLowerTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionOddTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConditionTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ConstTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.DefblockTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.DefcmdConstTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.DefcmdVarTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.DivTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.DoTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.EndOfProgramTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.EndOfStatementTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.EndTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.EqualsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.EvenTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ExpressionAddTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ExpressionSubsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ExpressionTermTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.FactorIdTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.FactorTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.GreaterEqualsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.GreaterTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.IdListTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.IdentifierTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.IfTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.LeftParenthesisTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.LowerEqualsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.LowerTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.MainBlockTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.MultTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.NumericalTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.OddTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ProcBlockTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ProcedureTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ProgTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ReadlnTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.RightParenthesisTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.SeparatorTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.SimpleTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtAssignmentTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtCallTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtIfTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtListTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtReadlnTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtStmtsBlockTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtWhileTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtWriteTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtWritelnTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StmtsBlockTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.StringTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.SubsTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.TermDivTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.TermFactorTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.TermMultTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.ThenTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.VarTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.WhileTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.WrParamsTailIdTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.WrParamsTailTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.WriteTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.WritelnTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.WrparamsIdTreeNode;
import edu.caece.langprocessor.syntax.tree.nodes.WrparamsStringTreeNode;

public class ParseTreePrettyPrinterVisitor implements TreeNodeVisitor {

	private static final char INDENT = '\t';
	private static final char NEW_LINE = '\n';
	private StringBuilder stbCodeView;
	private Tree tree;
	private int indentSize = 0;
	
	public ParseTreePrettyPrinterVisitor(Tree tree) {
		this.tree = tree;
		this.stbCodeView = new StringBuilder();
	}

	private void decreaseIndent() {
		indentSize--;
		if (indentSize < 0)
			indentSize = 0;
	}

	private void increaseIndent() {
		indentSize++;
	}

	private void navigateChildren(TreeNode node) {
		List<TreeNode> children = this.tree.getChildren(node);
		if (children != null) {
			for (TreeNode child : children) {
				child.accept(this);
			}
		}
	}

	public String print() {
		TreeNode node = this.tree.getRoot();
		if (node != null)
			node.accept(this);
		return stbCodeView.toString();
	}

	private void printIndentChars() {
		StringBuilder st = new StringBuilder("");
		for (int i = 1; i <= indentSize; i++)
			st.append(INDENT);
		stbCodeView.append(st.toString());
	}

	private void printNewLine() {
		stbCodeView.append(NEW_LINE);
	}

	private void printNode(TreeNode node) {
		stbCodeView.append(node.getGrammarItem().getValue());
	}

	private void printSpaceChar() {
		stbCodeView.append(" ");
	}

	public void visitAddTreeNode(AddTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();
	}

	public void visitAssignmentTreeNode(AssignmentTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();
	}

	public void visitBeginTreeNode(BeginTreeNode node) {
		if (stbCodeView.charAt(stbCodeView.length()-1) != INDENT)
			printIndentChars();
		printNode(node);
		printNewLine();
		increaseIndent();
	}

	public void visitCallTreeNode(CallTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitConditionEqualsTreeNode(ConditionEqualsTreeNode node) {
		navigateChildren(node);		
	}

	public void visitConditionGreaterTreeNode(ConditionGreaterTreeNode node) {
		navigateChildren(node);		
	} 

	public void visitConditionEvenTreeNode(ConditionEvenTreeNode node) {
		navigateChildren(node);		
	}

	public void visitConditionGreaterEqualsTreeNode(
			ConditionGreaterEqualsTreeNode node) {
		navigateChildren(node);		
	}

	public void visitConditionLowerEqualsTreeNode(
			ConditionLowerEqualsTreeNode node) {
		navigateChildren(node);		
	}

	public void visitConditionLowerTreeNode(ConditionLowerTreeNode node) {
		navigateChildren(node);		
	}

	public void visitConditionOddTreeNode(ConditionOddTreeNode node) {
		navigateChildren(node);		
	}

	public void visitConditionTreeNode(ConditionTreeNode node) {
		navigateChildren(node);		
	}

	public void visitConstTreeNode(ConstTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitDefblockTreeNode(DefblockTreeNode node) {
		navigateChildren(node);		
	}

	public void visitDefcmdConstTreeNode(DefcmdConstTreeNode node) {
		printIndentChars();
		navigateChildren(node);		
	}

	public void visitDefcmdVarTreeNode(DefcmdVarTreeNode node) {
		printIndentChars();
		navigateChildren(node);		
	}

	public void visitDivTreeNode(DivTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();
	}

	public void visitDoTreeNode(DoTreeNode node) {
		printSpaceChar();
		printNode(node);
		printNewLine();
	}

	public void visitEndOfProgramTreeNode(EndOfProgramTreeNode node) {
		if (stbCodeView.charAt(stbCodeView.length()-1 ) == NEW_LINE)
			stbCodeView.setLength(stbCodeView.length() -1);		
		printNode(node);
		printNewLine();
	}

	public void visitEndOfStatementTreeNode(EndOfStatementTreeNode node) {
		if (stbCodeView.charAt(stbCodeView.length()-1 ) == NEW_LINE)
			stbCodeView.setLength(stbCodeView.length() -1);
		printNode(node);
		printNewLine();
	}

	public void visitEndTreeNode(EndTreeNode node) {
		decreaseIndent();
		if (stbCodeView.charAt(stbCodeView.length()-1)  != NEW_LINE)
			printNewLine();
		printIndentChars();
		printNode(node);
		printNewLine();
	}

	public void visitEqualsTreeNode(EqualsTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();		
	}

	public void visitEvenTreeNode(EvenTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitExpressionAddTreeNode(ExpressionAddTreeNode node) {
		navigateChildren(node);
	}

	public void visitExpressionSubsTreeNode(ExpressionSubsTreeNode node) {
		navigateChildren(node);
	}

	public void visitExpressionTermTreeNode(ExpressionTermTreeNode node) {
		navigateChildren(node);
	}

	public void visitFactorIdTreeNode(FactorIdTreeNode node) {
		navigateChildren(node);
	}

	public void visitFactorTreeNode(FactorTreeNode node) {
		navigateChildren(node);
	}

	public void visitGreaterEqualsTreeNode(GreaterEqualsTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();		
	}

	public void visitGreaterTreeNode(GreaterTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();		
	}

	public void visitIdentifierTreeNode(IdentifierTreeNode node)  {
		printNode(node);
	}

	public void visitIdListTreeNode(IdListTreeNode node) {
		navigateChildren(node);
	}

	public void visitIfTreeNode(IfTreeNode node) {
		printNode(node);
		printSpaceChar();		
	}

	public void visitLeftParenthesisTreeNode(LeftParenthesisTreeNode node) {
		printSpaceChar();
		printNode(node);
	}

	public void visitLoweEqualsTreeNode(LowerEqualsTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();		
	}

	public void visitLowerTreeNode(LowerTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();		
	}

	public void visitMainBlockTreeNode(MainBlockTreeNode node) {
		printNewLine();
		navigateChildren(node);
	}

	public void visitMultTreeNode(MultTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();		
	}

	public void visitNumericalTreeNode(NumericalTreeNode node) {
		printNode(node);
	}

	public void visitOddTreeNode(OddTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitProcblockTreeNode(ProcBlockTreeNode node) {
		decreaseIndent();
		navigateChildren(node);
	}

	public void visitProcedureTreeNode(ProcedureTreeNode node) {
		printNewLine();
		increaseIndent();
		printNode(node);
		printSpaceChar();
	}

	public void visitProgTreeNode(ProgTreeNode node) {
		navigateChildren(node);
	}

	public void visitReadlnTreeNode(ReadlnTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitRightParenthesisTreeNode(RightParenthesisTreeNode node) {
		printNode(node);
		printSpaceChar();		
	}

	public void visitSeparatorTreeNode(SeparatorTreeNode node) {
		printNode(node);
		printSpaceChar();		
	}

	public void visitSimpleTreeNode(SimpleTreeNode node) {
		navigateChildren(node);
	}

	public void visitStmsCallTreeNode(StmtCallTreeNode node) {
		printIndentChars();
		navigateChildren(node);
	}

	public void visitStmtAssignmentTreeNode(StmtAssignmentTreeNode node) {
		printIndentChars();		
		navigateChildren(node);
	}

	public void visitStmtIfTreeNode(StmtIfTreeNode node) {
		printIndentChars();
		increaseIndent();
		navigateChildren(node);
		decreaseIndent();		
	}

	public void visitStmtListTreeNode(StmtListTreeNode node) {
		navigateChildren(node);	
	}

	public void visitStmtReadlnTreeNode(StmtReadlnTreeNode node) {
		printIndentChars();		
		navigateChildren(node);
	}

	public void visitStmtsBlockTreeNode(StmtsBlockTreeNode node) {
		navigateChildren(node);
	}

	public void visitStmtStmtsBlockTreeNode(StmtStmtsBlockTreeNode node) {
		printIndentChars();		
		navigateChildren(node);
	}

	public void visitStmtWhileTreeNode(StmtWhileTreeNode node) {
		printIndentChars();		
		increaseIndent();
		navigateChildren(node);
		decreaseIndent();
	}

	public void visitStmtWritelnTreeNode(StmtWritelnTreeNode node) {
		printIndentChars();
		navigateChildren(node);
	}

	public void visitStmtWriteTreeNode(StmtWriteTreeNode node) {
		printIndentChars();
		navigateChildren(node);
	}

	public void visitStringTreeNode(StringTreeNode node) {
		printNode(node);
	}

	public void visitSubsTreeNode(SubsTreeNode node) {
		printSpaceChar();
		printNode(node);
		printSpaceChar();		
	}

	public void visitTermDivTreeNode(TermDivTreeNode node) {
		navigateChildren(node);
	}

	public void visitTermFactorTreeNode(TermFactorTreeNode node) {
		navigateChildren(node);
	}

	public void visitTermMultTreeNode(TermMultTreeNode node) {
		navigateChildren(node);		
	}

	public void visitThenTreeNode(ThenTreeNode node) {
		printSpaceChar();
		printNode(node);
		printNewLine();
	}

	public void visitVarTreeNode(VarTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitWhileTreeNode(WhileTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitWritelnTreeNode(WritelnTreeNode node) {
		printNode(node);
	}

	public void visitWriteTreeNode(WriteTreeNode node) {
		printNode(node);
		printSpaceChar();
	}

	public void visitWrParamsTailTreeNode(WrParamsTailTreeNode node) {
		navigateChildren(node);
		}

	public void visitWrparamsIdTreeNode(WrparamsIdTreeNode node) {
		navigateChildren(node);
	}

	public void visitWrparamsStringTreeNode(WrparamsStringTreeNode node) {
		navigateChildren(node);
	}

	public void visitWrParamsTailIdTreeNode(WrParamsTailIdTreeNode node) {
		navigateChildren(node);
	}
}
